﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Text.RegularExpressions;
using System.Diagnostics;
// MakePanoForGoogle, Copyright Mirosław M. Mączka.  All Rights Reserved. This code released under the terms of the Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)

namespace MakePanoForGoogle
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //
        public const int DPI = 72;
        //
        public const int megaOfBytes = 1000000;
        public const int megaOfPixels = 1000000;
        //
        public const int MaxMegaBytes = 20;
        public const int MaxMegaPixels = 50;
        //
        //txtMaxMPixels
        internal int? formMaxMPixels
        {
            get { return _string2int(txtMaxMPixels.Text.Trim()); }
            set { txtMaxMPixels.Text = null != value ? ((int)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtMaxMegaBytes
        internal int? formMaxMegaBytes
        {
            get { return _string2int(txtMaxMegaBytes.Text.Trim()); }
            set { txtMaxMegaBytes.Text = null != value ? ((int)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtVerticalSpan
        internal double? formVerticalSpan
        {
            get { return _string2double(txtVerticalSpan.Text.Trim()); }
            set { txtVerticalSpan.Text = null != value ? ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtHorizontalSpan
        internal double? formHorizontalSpan
        {
            get { return _string2double(txtHorizontalSpan.Text.Trim()); }
            set { txtHorizontalSpan.Text = null != value ? ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtHorizontalFOV
        internal int? formPoseHeadingDegrees
        {
            get { return _string2int(txtPoseHeadingDegrees.Text.Trim()); }
            set { txtPoseHeadingDegrees.Text = null != value ? ((int)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtLatitude
        internal double? formLatitude
        {
            get { return _string2double(txtLatitude.Text.Trim()); }
            set { txtLatitude.Text = null != value ? ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtLongitude
        internal double? formLongitude
        {
            get { return _string2double(txtLongitude.Text.Trim()); }
            set { txtLongitude.Text = null != value ? ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture) : ""; }
        }
        //txtDayTaken
        internal DateTime? formDayTaken
        {
            get { return _string2datetime(txtDayTaken.Text.Trim()); }
            set { txtDayTaken.Text = null != value ? ((DateTime)value).ToString("yyyy-M-d") : ""; }
        }
        //
        internal string formDescription
        {
            get {return txtDescription.Text.Trim(); }
            set {txtDescription.Text = value.Trim(); }
        }
        //
        //
        private int? _string2int(string s)
        {
            int n=0;
            if (int.TryParse(s, out n) && n >= 0) return n;
            return null;
        }
        private double? _string2double(string s)
        {
            double d = 0.0;
            if (double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d)) return d;
            return null;
        }
        private DateTime? _string2datetime(string s)
        {
            DateTime dt;
            if (DateTime.TryParse(s, out dt)) return dt;
            return null;
        }
        //
        private bool isImageDropped = false;
        private bool isBusy = false;
        //
        public MainWindow()
        {
            InitializeComponent();
            //
            if(!Environment.Is64BitProcess)
            {
                win1.Background = Brushes.Pink;
            }
            //
            try
            {
                if (Properties.Settings.Default.MaxMegaBytes < 0)
                {
                    Properties.Settings.Default.MaxMegaBytes = MaxMegaBytes;
                    Properties.Settings.Default.Save();
                }
                if (Properties.Settings.Default.MaxMegaPixels < 0)
                {
                    Properties.Settings.Default.MaxMegaPixels = MaxMegaPixels;
                    Properties.Settings.Default.Save();
                }
            }
            catch { }
            //
            formMaxMegaBytes = Properties.Settings.Default.MaxMegaBytes;
            formMaxMPixels = Properties.Settings.Default.MaxMegaPixels;
            //
            cleanForm();
        }
        //##################################################################################################################################
        private void Gps_DragVerify(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string file = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
                if (isValidImageFile(file) || isValidTextFile(file))
                {
                    e.Effects = DragDropEffects.Copy;
                }
                else
                {
                    e.Effects = DragDropEffects.None;
                }
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
            e.Handled = true;
        }
        //---------------------------------------------------------------------------
        private void Gps_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                //
                e.Effects = DragDropEffects.Copy;
                //
                //
                string imageFileName = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
                string ext = System.IO.Path.GetExtension(imageFileName).ToUpper();
                //
                if (".TXT" == ext)
                {
                    string[] ll = System.IO.File.ReadAllLines(imageFileName);
                    handleTextFile(ll);
                }
                else
                {
                    if (!isValidImageFile(imageFileName))
                    {
                        e.Effects = DragDropEffects.None;
                        e.Handled = true;
                        return;
                    }

                    ExifUtils.Exif.GetExifData(imageFileName);
                    if (ExifUtils.Exif.IsGps)
                    {
                        formLatitude = ExifUtils.Exif.Latitude;
                        formLongitude = ExifUtils.Exif.Longitude;
                    }
                    if (ExifUtils.Exif.IsDate)
                    {
                        formDayTaken = ExifUtils.Exif.DayTaken;
                    }
                }
            }
            //
            e.Effects = DragDropEffects.None;
            e.Handled = true;
            //
        }
        //##################################################################################################################################
        private void image1_DragVerify(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                if (isValidImageFile(((string[])e.Data.GetData(DataFormats.FileDrop))[0]))
                {
                    e.Effects = DragDropEffects.Copy;
                }
                else
                {
                    e.Effects = DragDropEffects.None;
                }
            }
            else
            {
                e.Effects = DragDropEffects.None;
            }
            e.Handled = true;
        }
        //---------------------------------------------------------------------------
        bool isImageJpg = false;
        private void image1_Drop(object sender, DragEventArgs e)
        {
            //
            if(isBusy)
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
                return;
            }
            //
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string imageFileName = ((string[])e.Data.GetData(DataFormats.FileDrop))[0];
                //
                if (!isValidImageFile(imageFileName))
                {
                    e.Effects = DragDropEffects.None;
                    e.Handled = true;
                    return;
                }
                e.Effects = DragDropEffects.Copy;
                //
                //
                isImageJpg = isJpg(imageFileName);
                bool isImageTiff = isTiff(imageFileName);
                //
                showFile(imageFileName);
                cleanForm();
                //
                if(!Properties.Settings.Default.WasTiffWaning && !isImageTiff)
                {
                    e.Effects = DragDropEffects.None;
                    Properties.Settings.Default.WasTiffWaning = true;
                    MessageBox.Show("TIFF is the best format!", "Image from ICE", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
                //
            }
            //
            e.Effects = DragDropEffects.None;
            e.Handled = true;
        }
        //##################################################################################################################################

        private void image1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            image1.IsEnabled = false;
            Point p = e.GetPosition(image1);
            double width = image1.ActualWidth;
            double x = p.X;
            image1.IsEnabled = true;
            double position = Math.Min(1.0, Math.Round(x / width,3));
            //
            int northDirectionDegrees;
            if (position >= 0.5)
            {
                position -= 0.5;
            }
            else
            {
                position += 0.5;
                //northDirectionDegrees = (int)(360.0 * position - 180.0);
            }
            northDirectionDegrees = (int)(360.0 * position);
            formPoseHeadingDegrees = Math.Max(0, Math.Min(359, northDirectionDegrees));
            //int fov =  (int)((x / width) * 360.0);
            //if(fov < 5){fov=0;}
            //else if (fov > 355) { fov = 0; }
            //
            //
        }
        //##################################################################################################################################
        private void textChanged_FloatNumber(object sender, TextChangedEventArgs e)
        {
            TextBox tb = sender as TextBox;
            //Regex regex = new Regex("^[0-9]*[.,]?[0-9]+$");
            //e.Handled = regex.IsMatch(tb.Text);
            //http://wpftutorial.co.in/WPF-Numeric-Textbox-Control
            if (string.IsNullOrEmpty(tb.Text))
            {
                tb.Tag = "";
            }
            else
            {
                double num = 0;
                bool success = double.TryParse(tb.Text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture,out num);
                if (success & num >= 0)
                {
                    tb.Text.Trim();
                    tb.Tag = tb.Text;
                }
                else
                {
                    tb.Text = (string)tb.Tag;
                    tb.SelectionStart = tb.Text.Length;
                }
            }
           
        }

        private void txtPreview_IntegerNumber(object sender, TextCompositionEventArgs e)
        {
            Regex regex = new Regex("[^0-9]+");
            e.Handled = regex.IsMatch(e.Text);
        }

        private void txtMaxMega_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Z && Keyboard.Modifiers == ModifierKeys.Control)
            {
                formMaxMegaBytes = MaxMegaBytes;
                formMaxMPixels = MaxMegaPixels;
                e.Handled = true;
                return;
            }
            e.Handled = false;
        }

        //##################################################################################################################################
        //
        internal async void do_Click(object sender, RoutedEventArgs e)
        {
            if (isBusy || !isImageDropped)
            {
                e.Handled = true;
                return;
            }
            //
            //-----------------------------------------------
            //
            if (null == formMaxMPixels || formMaxMPixels <=0)
            {
                MessageBox.Show("Invalid value of MegaPixels", "formMaxMPixels", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if (null == formMaxMegaBytes || formMaxMegaBytes<=0)
            {
                MessageBox.Show("Invalid value of MegaBytes", "formMaxMegaBytes", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if (null == formVerticalSpan || formVerticalSpan < 1.0 || formVerticalSpan>180.0)
            {
                MessageBox.Show("Invalid value of VerticalSpan", "formVerticalSpan", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if (null == formHorizontalSpan || formHorizontalSpan < 1.0 || formHorizontalSpan>360.0)
            {
                MessageBox.Show("Invalid value of HorizontalSpan", "formHorizontalSpan", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }

            if (null == formPoseHeadingDegrees || formPoseHeadingDegrees < 0 || formPoseHeadingDegrees>360)
            {
                MessageBox.Show("Invalid value of HorizontalFOV", "formHorizontalFOV", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if(null==formLatitude)
            {
                MessageBox.Show("Invalid value of Latitude", "formLatitude", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if(null==formLongitude)
            {
                MessageBox.Show("Invalid value of Longitude", "formLongitude", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            if(null==formDayTaken)
            {
                MessageBox.Show("Invalid value of DayTaken", "formDayTaken", MessageBoxButton.OK, MessageBoxImage.Error);
                e.Handled = true;
                return;
            }
            //if(""==formDescription)
            //{
            //}

            //
            //
            //
            //-----------------------------------------------
            //
            System.Drawing.Image img = null;
            try
            {
                isBusy = true;
                //
                //buttonDo.IsEnabled = false;
                //win1.IsEnabled = false;
                buttonDo.Visibility = System.Windows.Visibility.Hidden;
                buttonDo.Refresh();
                win1.Cursor = Cursors.Wait;
                //
                //
                //if (null == imgHelper)
                {
                    //System.Windows.Resources.StreamResourceInfo imageInfo = System.Windows.Application.GetResourceStream(new Uri("pack://application:,,,/props.jpg"));
                    //imgHelper = System.Drawing.Image.FromStream(imageInfo.Stream);
                }
                //
                GC.Collect();
                //
                Uri uri = new Uri(image1.Source.ToString());
                //
                //
                img = System.Drawing.Image.FromFile(uri.LocalPath);
                long inputFileLength = (new System.IO.FileInfo(uri.LocalPath)).Length;
                //
                ImageManipulations.ResetInfo();
                //
                if (0 != formMaxMPixels)
                {
                    img = await ImageManipulations.ResizeIfNeeded(img, (int)formMaxMPixels);
                }
                //
                //
                //
                if ("" == formDescription)
                {
                    Random rnd = new Random((int)(DateTime.Now.Ticks&0xFFFF));
                    formDescription = string.Format("GPano{0}", rnd.Next(100, 999));
                }
                //
                string cleanFileName = new string(formDescription.Where(c => !System.IO.Path.GetInvalidFileNameChars().Contains(c)).ToArray());
                cleanFileName = cleanFileName.Replace(" ", "");
                string outFileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(uri.LocalPath), string.Format("photosphere({0}).jpg", cleanFileName));
                //
                //
                //
                if (0 == formMaxMPixels && 0 == formMaxMegaBytes)
                {
                    //copy
                    if (isImageJpg)
                    {
                        saveByCopy(uri.LocalPath, outFileName);
                    }
                    else
                    {
                        await ImageManipulations.SaveBitmapAsIs(img as System.Drawing.Bitmap, outFileName);
                    }
                }
                else if (0 == formMaxMPixels && 0 != formMaxMegaBytes)
                {
                    // NO resize, match length
                    await ImageManipulations.SaveBitmapWithSize(img as System.Drawing.Bitmap, outFileName, (int)formMaxMegaBytes);
                }
                else if (0 != formMaxMPixels && 0 == formMaxMegaBytes)
                {
                    // resize, NO length match
                    if(ImageManipulations.IsResized)
                    {
                        // PIXELS zmienione
                        await ImageManipulations.SaveBitmapAsIs(img as System.Drawing.Bitmap, outFileName);
                    }
                    else
                    {
                        //copy
                        if (isImageJpg)
                        {
                            saveByCopy(uri.LocalPath, outFileName);
                        }
                        else
                        {
                            await ImageManipulations.SaveBitmapAsIs(img as System.Drawing.Bitmap, outFileName);
                        }
                    }
                }
                else //(0 != formMaxMPixels && 0 != formMaxMegaBytes)
                {
                    await ImageManipulations.SaveBitmapWithSize(img as System.Drawing.Bitmap, outFileName, (int)formMaxMegaBytes);
                }
                //
                //
                MetadataHandling.WriteMetadata(outFileName, formDescription, (DateTime)formDayTaken, (double)formLatitude, (double)formLongitude, (double)formHorizontalSpan, (double)formVerticalSpan, img.Width, img.Height, (int)formPoseHeadingDegrees);
                //
                string info = "Image:";
                info += string.Format("Resized: {0}", ImageManipulations.IsResized ? "YES" : "no");
                info += Environment.NewLine;
                info += string.Format("Quality: {0}", ImageManipulations.ImageQuality);
                MessageBox.Show(info, "Do", MessageBoxButton.OK, MessageBoxImage.Information);
                //
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message, "Do");
            }
            finally
            {
                if(null!=img)img.Dispose();
                GC.Collect();
                win1.Cursor = Cursors.Arrow;
                //win1.IsEnabled = true;
                buttonDo.Visibility = System.Windows.Visibility.Visible;
                //buttonDo.IsEnabled = true;
                buttonDo.Refresh();
                //
                isBusy = false;
            }
            //
        }
        //##################################################################################################################################
        private void saveByCopy(string inFilename, string outFileName)
        {
            if (System.IO.Path.GetFullPath(inFilename).ToUpper() != System.IO.Path.GetFullPath(outFileName).ToUpper())
            {
                System.IO.File.Copy(inFilename, outFileName, true);
            }
            else
            {
                if (System.IO.File.Exists(outFileName)) System.IO.File.Delete(outFileName);
                System.IO.File.Move(inFilename, outFileName);
            }
        }

        public async Task CopyFileAsync(string sourcePath, string destinationPath)
        {
            using (System.IO.Stream source = System.IO.File.Open(sourcePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
            {
                using (System.IO.Stream destination = System.IO.File.Create(destinationPath))
                {
                    await source.CopyToAsync(destination);
                }
            }
        }
        //##################################################################################################################################
        private void buttonICE_Click(object sender, RoutedEventArgs e)
        {
            Button b = sender as Button;
            if (null != b)
            {
                Process.Start(new ProcessStartInfo((new Uri(b.ToolTip.ToString())).ToString()));
            }
            e.Handled = true;

        }
        private void buttonPhotos_Click(object sender, RoutedEventArgs e)
        {
            Button b = sender as Button;
            if (null != b)
            {
                Process.Start(new ProcessStartInfo((new Uri(b.ToolTip.ToString())).ToString()));
            }
            e.Handled = true;
        }

        private void buttonViews_Click(object sender, RoutedEventArgs e)
        {

            Button b = sender as Button;
            if (null != b)
            {
                Process.Start(new ProcessStartInfo((new Uri(b.ToolTip.ToString())).ToString()));
            }
            e.Handled = true;
        }

        internal void cleanForm()
        {
            formVerticalSpan = null;
            formHorizontalSpan = 360.0;
            formPoseHeadingDegrees = 0;
            formLatitude = null;
            formLongitude = null;
            formDayTaken = DateTime.Now;
            formDescription = "";
        }

        private void win1_Closed(object sender, EventArgs e)
        {
            Properties.Settings.Default.MaxMegaBytes = null == formMaxMegaBytes ? 0 : (int)formMaxMegaBytes; ;
            Properties.Settings.Default.MaxMegaPixels = null == formMaxMPixels ? 0 : (int)formMaxMPixels;
            //
            Properties.Settings.Default.Save();
        }



    }
}
